#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdbool.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>
#include<pwd.h>
#include<grp.h>
bool show_all=false;
bool show_list=false;
bool show_recursion=false;
void do_ls_r(char *pathname,int depth);
void do_ls(char *pathname);
int judge_dir(char *pathname);
void do_showlist(DIR *dp);
void mode_to_letters(int mode,char str[]);
char *uid_to_name(uid_t uid);
char *gid_to_name(gid_t gid);


int main(int argc,char *argv[])
{
    int opt;
    while((opt=getopt(argc,argv,"arl"))!=-1){
        switch(opt){
            case 'a':
                show_all=true;
                break;
            case 'r':
                show_recursion=true;
                break;
            case 'l':
                show_list=true;
                break;
            case '?':
                printf("unknow option :%c\n",optopt);
                exit(1);
        }
    }
    if(show_recursion){
        if(argc==optind)
          do_ls_r(".",4);
        else{
            for(;optind<argc;optind++){
                if(judge_dir(argv[optind])){
                    printf("%s:\n",argv[optind]);
                    do_ls_r(argv[optind],4);
                }
                else{
                    printf("not a directory!\n");
                    exit(1);
                }
            }
        }
    }
    else{
        if(argc==optind)
          do_ls(".");
        else{
            for(;optind<argc;optind++){
                if(judge_dir(argv[optind])){
                    printf("%s:\n",argv[optind]);
                    do_ls(argv[optind]);
                }
                else{
                    printf("not a directory!\n");
                    exit(1);
                }
            }
        }
    }

}

int judge_dir(char *pathname)
{
    struct stat statbuf;
    if((lstat(pathname,&statbuf))<0){
      printf("error in lstat!\n");
      exit(1);
    }
    if(S_ISDIR(statbuf.st_mode))
      return 1;
    else
      return -1;
}
void do_ls_r(char *pathname,int depth)
{
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
    if((dp=opendir(pathname))==NULL){
        printf("can't open dir:%s\n",pathname);
        exit(1);
    }
    chdir(pathname);
    while((entry=readdir(dp))!=NULL){
        lstat(entry->d_name,&statbuf);
        if(S_ISDIR(statbuf.st_mode)){
            if(strcmp(".",entry->d_name)==0||strcmp("..",entry->d_name)==0)
              continue;
            printf("%*s%s/\n",depth," ",entry->d_name);
            do_ls_r(entry->d_name,depth+4);
        }
        else
          printf("%*s%s\n",depth," ",entry->d_name);
    }
    chdir("..");
    closedir(dp);

}
void do_ls(char *pathname)
{
    DIR *dp;
    struct dirent *entry;
    if((dp=opendir(pathname))==NULL){
            printf("can't open dir:%s\n",pathname);
            exit(1);
        }
    if(show_list){
        chdir(pathname);
        do_showlist(dp);
    }
    else{
            while((entry=readdir(dp))!=NULL){
            if(!show_all)
              if(*(entry->d_name)=='.')
                continue;
            printf("%s\n",entry->d_name);
        }
        closedir(dp);
    }
}
void do_showlist(DIR *dp)
{
    struct dirent *entry;
    struct stat statbuf;
    char l_mode[11];
    while((entry=readdir(dp))!=NULL){
        if((lstat(entry->d_name,&statbuf))<0){
            printf("error :in lstat\n");
            exit(1);
        }
        if(!show_all)
          if(*(entry->d_name)=='.')
            continue;
        mode_to_letters(statbuf.st_mode,l_mode);
        printf("%s",l_mode);
        printf("%4d ",(int)statbuf.st_nlink);
        printf("%-8s ",uid_to_name(statbuf.st_uid));
        printf("%-8s ",gid_to_name(statbuf.st_gid));
        printf("%8ld ",(long)statbuf.st_size);
        printf("    %s ",entry->d_name);
        printf("\n");
    }
}
void mode_to_letters(int mode,char str[])
{
    strcpy(str,"----------");
    if(S_ISDIR(mode)) str[0]='d';
    if(S_ISCHR(mode)) str[0]='c';
    if(S_ISBLK(mode)) str[0]='b';

    if(mode&S_IRUSR)  str[1]='r';
    if(mode&S_IWUSR)  str[2]='w';
    if(mode&S_IXUSR)  str[3]='x';

    if(mode&S_IRGRP)  str[4]='r';
    if(mode&S_IWGRP)  str[5]='w';
    if(mode&S_IXGRP)  str[6]='x';

    if(mode&S_IROTH)  str[7]='r';
    if(mode&S_IWOTH)  str[8]='w';
    if(mode&S_IXOTH)  str[9]='x';
}

char *uid_to_name(uid_t uid)
{
    struct passwd *getpwuid(),*pw_ptr;
    if((pw_ptr=getpwuid(uid))!=NULL)
      return (pw_ptr->pw_name);
    else
      return NULL;
}
char *gid_to_name(gid_t gid)
{
    struct group *getgrid,*grp_ptr;
    if((grp_ptr=getgrgid(gid))!=NULL)
      return (grp_ptr->gr_name);
    else
      return NULL;
}
